using System;

namespace SharpMud.Net.Telnet.Session.DataProcessors.MCCP
{
	public class CompressionLayer : System.IO.IDataProcessor
	{
		#region Private Variables


		private System.Collections.IQueue<byte>				_InputQueue;
		private System.Collections.IQueue<byte>				_OutputQueue;
		
		private System.IO.ByteQueueStream								_OutputStream;
		private System.IO.Stream										_CompressionStream;
		private bool													_CompressionInProgress;
		#endregion
		
		#region Constructors
		public CompressionLayer()
		{
			//Initialization is done in IDataProcessor.Initialize()
		}
		#endregion

		#region Public Properties
		public bool IsCompressionInProgress
		{
			get
			{
				return _CompressionInProgress;
			}
		}
		public bool IsCompressionSuspended
		{
			get
			{
				if(_CompressionInProgress==false && _CompressionStream!=null)
					return true;
				else if(_CompressionInProgress==false && _CompressionStream==null)
					throw new InvalidOperationException("There is no MCCP compression in progress to be suspended.");
				else
					return false;					
			}
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Start compression of outgoing data. Does not insert any data to indicate start of compression.
		/// </summary>
		public void Start()
		{
			if(_CompressionInProgress==true)
			{
				System.Diagnostics.Debug.Assert(_CompressionStream!=null,"_CompressionStream!=null");
				throw new InvalidOperationException("MCCP compression is already in progress.");
			}
			else if(_CompressionInProgress==false && _CompressionStream!=null)
			{
				_CompressionInProgress = true;
			}
			else if(_CompressionInProgress==false && _CompressionStream==null)
			{
				_CompressionInProgress = true;
                _CompressionStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(_OutputStream);
			}
			return;
		}
		/// <summary>
		/// Stop compression of outgoing data. Does not insert any data to indicate the end of compression.
		/// </summary>
		public void Stop()
		{
			if(_CompressionStream==null)
			{
				System.Diagnostics.Debug.Assert(_CompressionInProgress==false,"_CompressionInProgress==false");
				throw new InvalidOperationException("MCCP compression is already stopped.");
			}
			else if (_CompressionStream!=null)
			{
				_CompressionInProgress = false;
				_CompressionStream = null;
				return;
			}
			System.Diagnostics.Debug.Assert(false);
		}
		/// <summary>
		/// Suspends compression of outgoing data. Does not insert any data to indicate that compression is suspending.
		/// </summary>
		public void Suspend()
		{
			if(_CompressionStream==null)
			{
				throw new System.InvalidOperationException("There is no MCCP compression in progress to suspend.");
			}
			if(_CompressionInProgress==false)
			{
				throw new System.InvalidOperationException("MCCP compression is already suspended.");
			}
			_CompressionInProgress = false;

		}
		#endregion

		#region IDataProcessor Members

		public System.Collections.IQueue<byte> OutputQueue
		{
			get
			{
				return _OutputQueue;
			}
		}

		public System.Collections.IQueue<byte> InputQueue
		{
			get
			{
				return _InputQueue;
			}
		}

		public void Initialize(System.Collections.IQueue<byte> inputQueue, System.Collections.IQueue<byte> outputQueue)
		{
			_InputQueue = inputQueue;
			_OutputQueue = outputQueue;
			_OutputStream = new System.IO.ByteQueueStream(this._OutputQueue,false,true);
			_CompressionStream = null;
			_CompressionInProgress = false;
		}

		public void Pulse()
		{
			if(this._InputQueue.Count>0)
			{
				byte[] bs;
				lock(InputQueue)
				{
					bs = _InputQueue.Dequeue(_InputQueue.Count);
				}
				lock(OutputQueue)
				{
					lock(this)
					{
							
						if(_CompressionInProgress)
						{
							_CompressionStream.Write(bs,0,bs.Length);
                            if (_CompressionStream is ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream)
                            {
                                (_CompressionStream as ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream).Finish();
                                _CompressionStream.Flush();
                                _CompressionStream = null;

                                _CompressionStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(_OutputStream);
                            }
						}
						else
						{
							_OutputStream.Write(bs,0,bs.Length);
						}
					}
				}
			}
		}

		#endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_CompressionStream != null)
            {
                _CompressionStream.Dispose();
                _CompressionStream = null;
            }
        }

        #endregion
    }
}
